package logger

import (
	"fmt"
	"gin_http/utils/config/modules"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	confpkg "gin_http/utils/config"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

var (
	modLoggers sync.Map
)

// -----------------------------
// 合并 default + module 配置
// -----------------------------
func mergeConfig(module string) modules.LoggerConfigModule {
	cfg := confpkg.GetConfig().Logger

	// 默认配置
	result := cfg.Default

	// module 覆盖配置
	if mod, ok := cfg.Modules[module]; ok {
		if mod.Path != "" {
			result.Path = mod.Path
		}
		if mod.Level != "" {
			result.Level = mod.Level
		}
		if mod.MaxSize != 0 {
			result.MaxSize = mod.MaxSize
		}
		if mod.MaxAge != 0 {
			result.MaxAge = mod.MaxAge
		}
		if mod.MaxBackups != 0 {
			result.MaxBackups = mod.MaxBackups
		}
		if mod.Console != nil {
			result.Console = mod.Console
		}
		if mod.Compress != nil {
			result.Compress = mod.Compress
		}
	}

	result.Service = cfg.Default.Service

	return result
}

// GetLogger 获取默认日志记录器
// 返回值: *zap.Logger 默认日志记录器
func GetLogger() *zap.Logger {
	// 使用特殊模块名 "__default__" 来复用 modLoggers 的缓存
	const defaultModule = "__default__"

	if v, ok := modLoggers.Load(defaultModule); ok {
		return v.(*zap.Logger)
	}

	// 如果不存在，则创建一个默认日志记录器
	l := newModuleLogger("") // 空字符串表示没有模块名
	modLoggers.Store(defaultModule, l)
	return l
}

// GetModuleLogger 获取指定模块的日志记录器
// 如果模块已存在，则返回已存在的日志记录器
// 如果模块不存在，则创建一个新的日志记录器并存储
//
// 参数:
//
//	module: 模块名称字符串
//
// 返回值:
//
//	*zap.Logger: 指定模块的日志记录器指针
func GetModuleLogger(module string) *zap.Logger {
	// 从modLoggers中尝试获取已存在的日志记录器
	// Load方法会返回存储的值和是否存在的布尔值
	if v, ok := modLoggers.Load(module); ok {
		// 如果存在，直接返回已存在的日志记录器
		return v.(*zap.Logger)
	}

	// 如果不存在，创建一个新的模块日志记录器
	l := newModuleLogger(module)
	// 将新创建的日志记录器存储到modLoggers中
	modLoggers.Store(module, l)
	// 返回新创建的日志记录器
	return l
}

// newModuleLogger 创建一个新的模块日志记录器
// @param module 模块名称，用于标识日志来源
// @return *zap.Logger 返回配置好的zap日志记录器实例
func newModuleLogger(module string) *zap.Logger {
	// 合并日志配置，将模块特定的配置与全局配置合并
	cfg := mergeConfig(module)

	// 目录：path/module/YYYY-MM/
	base := filepath.Join(cfg.Path, module)
	month := time.Now().Format("2006-01")
	logDir := filepath.Join(base, month)

	if err := os.MkdirAll(logDir, 0755); err != nil {
		log.Fatalf("failed to create log dir: %v", err)
	}

	logFile := filepath.Join(logDir, fmt.Sprintf("%02d.log", time.Now().Day()))

	fileWriter := zapcore.AddSync(&lumberjack.Logger{
		Filename:   logFile,
		MaxSize:    cfg.MaxSize,
		MaxAge:     cfg.MaxAge,
		MaxBackups: cfg.MaxBackups,
		Compress:   *cfg.Compress,
	})

	encoder := zapcore.NewJSONEncoder(zapcore.EncoderConfig{
		TimeKey:      "time",
		LevelKey:     "level",
		MessageKey:   "msg",
		CallerKey:    "caller",
		EncodeCaller: zapcore.ShortCallerEncoder,
		EncodeTime:   zapcore.ISO8601TimeEncoder,
		EncodeLevel:  zapcore.LowercaseLevelEncoder,
	})

	var zapLevel zapcore.Level
	_ = zapLevel.UnmarshalText([]byte(cfg.Level))

	fileCore := zapcore.NewCore(encoder, fileWriter, zapLevel)

	var core zapcore.Core = fileCore

	// 控制台输出是否开启
	if *cfg.Console {
		consoleCore := zapcore.NewCore(
			zapcore.NewConsoleEncoder(zapcore.EncoderConfig{
				TimeKey:     "time",
				LevelKey:    "level",
				EncodeLevel: zapcore.LowercaseColorLevelEncoder,
				MessageKey:  "msg",
				EncodeTime:  zapcore.ISO8601TimeEncoder,
			}),
			zapcore.AddSync(os.Stdout),
			zapLevel,
		)
		core = zapcore.NewTee(core, consoleCore)
	}

	return zap.New(
		core,
		zap.AddCaller(),
		zap.AddCallerSkip(1),
	).
		With(
			zap.String("service", cfg.Service),
			zap.String("module", module),
		)
}
